using JT808Protocol.Models.BodyParserStrategy.Location;
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Text;

namespace JT808Protocol.Models
{
    /// <summary>
    /// 消息体解析提供类
    /// </summary>
    public class JT808MsgBodyParserFactory
    {
        /// <summary>
        /// 消息体解析器缓存
        /// </summary>
        private static ConcurrentDictionary<ushort, object> dicMsgBodyParser;

        /// <summary>
        /// 位置附加信息解析器缓存
        /// </summary>
        private static ConcurrentDictionary<int, object> dicLocationAddtionParser;

        private static JT808MsgBodyParserFactory _instance = null;
        private static object _syncObj = new object();

        private static JT808MsgBodyParserFactory Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_syncObj)
                    {
                        if (_instance == null)
                        {
                            _instance = new JT808MsgBodyParserFactory();
                            //顺便注册编码，避免GB2312无法解析
                            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                        }
                    }
                }
                return _instance;
            }
        }

        public JT808MsgBodyParserFactory()
        {
            InitBodyParsers();
            InitLocationAddtionParser();
        }

        /// <summary>
        /// 获取消息体解析器
        /// </summary>
        /// <param name="msgId"></param>
        /// <returns></returns>
        public static IJT808Body GetBodyParser(EnumMessageType msgType)
        {
            return Instance.getBodyParser((ushort)msgType);
        }

        /// <summary>
        /// 获取消息体解析器
        /// </summary>
        /// <param name="msgId"></param>
        /// <returns></returns>
        public static IJT808Body GetBodyParser(ushort msgType)
        {
            return Instance.getBodyParser(msgType);
        }

        /// <summary>
        /// 获取位置附加信息解析器
        /// </summary>
        /// <param name="additionType"></param>
        /// <returns></returns>
        public static ILocationAddition GetLocationAddtionParser(EnumLocationAddition additionType)
        {
            return Instance.getLocationAddtionParser((int)additionType);
        }

        #region 私有方法

        private void InitLocationAddtionParser()
        {
            dicLocationAddtionParser = new ConcurrentDictionary<int, object>();
            var assembly = Assembly.Load("JT808Protocol.Models");
            var allTypes = assembly.GetTypes();
            foreach (var type in allTypes)
            {
                if (type.IsClass && !type.IsAbstract
                    && type.IsAssignableTo(typeof(ILocationAddition)))
                {
                    var implementObj = Activator.CreateInstance(type);
                    var addiId = type.Name.Substring(type.Name.Length - 2);
                    var addiIdKey = Convert.ToUInt16(addiId, 16);

                    ILocationAddition addition = (ILocationAddition)implementObj;

                    dicLocationAddtionParser.TryAdd(addiIdKey, addition);
                }
            }
        }

        private void InitBodyParsers()
        {
            dicMsgBodyParser = new ConcurrentDictionary<ushort, object>();
            var assembly = Assembly.Load("JT808Protocol.Models");
            var allTypes = assembly.GetTypes();
            foreach (var type in allTypes)
            {
                if (type.IsClass && !type.IsAbstract && type.Name.Contains("0x")
                    && type.IsAssignableTo(typeof(IJT808Body)))
                {
                    try
                    {
                        var implementObj = Activator.CreateInstance(type);
                        var msgId = type.Name.Substring(type.Name.Length - 4);
                        var msgIdKey = Convert.ToUInt16(msgId, 16);

                        dicMsgBodyParser.TryAdd(msgIdKey, implementObj);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("InitBodyParsers:" + ex.Message + ",StackTrace:" + ex.StackTrace);
                    }
                }
            }
        }

        private IJT808Body getBodyParser(ushort msgId)
        {
            object bodyParser;
            if (dicMsgBodyParser.TryGetValue(msgId, out bodyParser))
            {
                return bodyParser as IJT808Body;
            }
            else
            {
                return null;
            }
        }

        private ILocationAddition getLocationAddtionParser(int addtionId)
        {
            object addtionParser;
            if (dicLocationAddtionParser.TryGetValue(addtionId, out addtionParser))
            {
                (addtionParser as ILocationAddition).AdditionID = (EnumLocationAddition)addtionId;
                return addtionParser as ILocationAddition;
            }
            else
            {
                return null;
            }
        }

        #endregion
    }
}